using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using Utilities;
using Services;
using System.Threading;

namespace ChainAlg
{

    
    public enum ComputeState
    {
        Running,
        Success,
        Failed,
        //NotRunnign
    }
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        List<BodyData> bodies;
        Texture2D point, circle;
        //bool computeComplete;
        ComputeState state;
        Random rnd;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 480;
            // Frame rate is 30 fps by default for Windows Phone.
            //TargetElapsedTime = TimeSpan.FromTicks(333333);
            IsMouseVisible = true;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            point = Content.Load<Texture2D>("point");
            circle = Content.Load<Texture2D>("circle");
            bodies = new List<BodyData>();
            rnd = new Random();
            //bodies.Add(new BodyData()
            //{
            //    pos=new Vector3(50,0,50),
            //    range=100
            //});
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        KeyboardState pkb = Keyboard.GetState();
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            {
                ComputeList();
                state = ComputeState.Running;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Enter) && pkb.IsKeyUp(Keys.Enter))
            {
                ComputeList();
                state = ComputeState.Running;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Space) && pkb.IsKeyUp(Keys.Space))
            {
                if (bodies.Count > 0)
                    Verify(bodies[0]);
                    //Verify();
            }
            if (Keyboard.GetState().IsKeyDown(Keys.F) && pkb.IsKeyUp(Keys.F))
            {
                Reset();
                if (bodies.Count > 0)
                    Verify(bodies[rnd.Next(bodies.Count-1) +1]);
                //Verify();
            }
            if (Keyboard.GetState().IsKeyDown(Keys.R) && pkb.IsKeyUp(Keys.R))
            {
                Reset();
            }
            pkb = Keyboard.GetState();
            // TODO: Add your update logic here
            //if(state==ComputeState.Success)
            //    state
            if (state == ComputeState.Failed)
                ComputeList();
            base.Update(gameTime);
        }
        protected override void OnExiting(object sender, EventArgs args)
        {
            if (t != null)
                t.Abort();
            base.OnExiting(sender, args);
        }
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here

            spriteBatch.Begin();

            if (bodies.Count > 0)
            {
                var b = bodies[0];
                spriteBatch.Draw(point, new Rectangle((int)b.pos.X - 5, (int)b.pos.Z - 5, 10, 10), b.reachead ? Color.Green : Color.White);
                spriteBatch.Draw(circle, new Rectangle((int)(b.pos.X - b.range), (int)(b.pos.Z - b.range), (int)b.range * 2, (int)b.range * 2), Color.Blue);
                for(int i=1;i<bodies.Count;i++)
                {
                    b = bodies[i];
                    spriteBatch.Draw(point, new Rectangle((int)b.pos.X - 5, (int)b.pos.Z - 5, 10, 10), b.reachead ? Color.Green : Color.Black);
                    spriteBatch.Draw(circle, new Rectangle((int)(b.pos.X - b.range), (int)(b.pos.Z - b.range), (int)b.range * 2, (int)b.range * 2), Color.Red);
                }
            }
            spriteBatch.End();
            base.Draw(gameTime);
        }

        Thread t = null;
        public void ComputeList()
        {
            //computeComplete = false;
            if (t != null && t.ThreadState== ThreadState.Running)
            {
                t.Abort();
                t = null;
            }
            bodies.Clear();
            t = new Thread(() =>
            {

                //Alg.computeList(bodies, 800, 480, 10, 0, ComputeComplete);
                Alg.computeList(bodies, 800, 480, 20, 10, 0, 2, ComputeComplete);
            });

            t.Start();
        }
        public void ComputeComplete(bool success)
        {
            Console.WriteLine(success);
            if (success)
                state = ComputeState.Success;
            else
                state = ComputeState.Failed;
            if (t != null)
            {
                t.Abort();
                t = null;
            }
            
            
        }
        public void Reset()
        {
            bodies.ForEach(b => b.reachead = false);
        }
        //public void Verify()
        //{
        //    for (int i = 0; i < bodies.Count; i++)
        //    {
        //        var b = bodies[i];
        //        for (int y = 0; y < bodies.Count; y++)
        //        {
        //            if (i != y)
        //            {

        //            }
        //        }
        //    }
        //}
        public void Verify(BodyData start)
        {
            start.reachead = true;
            //for (int i = 0; i < bodies.Count; i++)
            //{
            //    var b = bodies[i];
            //    if (b != start && Vector3.Distance(start.pos, b.pos) <= start.range)
            //        Verify(b);
            //}
            start.childs.ForEach(b =>
            {
                var dist = Vector3.Distance(start.pos, b.pos);
                if (dist <= start.range)
                    Verify(b);
            });
        }
    }
}
